package com.market.web.model;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.faces.context.FacesContext;
import javax.faces.model.ListDataModel;

import com.market.domain.BaseObject;
import com.market.util.CollectionUtil;

public abstract class GenericModel<BO extends BaseObject> extends
		ListDataModel<BO> {
	private List<BO> list = new ArrayList<BO>();
	private List<BO> filteredList;
	
	private BO current;
	private boolean isInitialized = false;

	public GenericModel() {
		super();
	}

	public GenericModel(List<BO> list) {
		super();
		this.setList(list);
	}

	protected String getMessage(String key, Locale locale) {
		java.util.ResourceBundle resBundle = ResourceBundle.getBundle(
				"com.market.messages", locale);
		return resBundle.getString(key);
	}

	protected String getMessage(String key) {
		Locale locale = FacesContext.getCurrentInstance().getViewRoot()
				.getLocale();
		java.util.ResourceBundle resBundle = ResourceBundle.getBundle(
				"com.market.messages", locale);
		return resBundle.getString(key);
	}

	
	
	public void clearSelection() {
		for (BO bo : this.list) {
			bo.setSelected(false);
		}
	}

	public boolean getIsInitialized() {
		return isInitialized;
	}

	public void setIsInitialized(boolean isInitialized) {
		this.isInitialized = isInitialized;
	}
	

	public List<BO> getList() {
		return list;
	}
	
	public void setList(List<BO> list) {
		this.list = list;
		if (this.list.size() > 0) {
			this.setCurrent(this.list.get(0));
		}
	}
	

	public List<BO> getFilteredList() {
		return filteredList;
	}

	public void setFilteredList(List<BO> filteredList) {
		this.filteredList = filteredList;
	}
	
	


	/**
	 * 
	 * @return A list of all objects in model, but remove new objects from it;
	 */
	public List<BO> getListWithoutNew() {
		List<BO> listWithoutNew = new ArrayList<BO>();
		for (BO bo : this.list) {
			if (bo.getIsNew() == Boolean.FALSE) {
				listWithoutNew.add(bo);
			}
		}
		return listWithoutNew;
	}

	
	public BO getCurrent() {
		if (this.current == null) { // If current is null, create an empty new
									// object and set it to current;
			BO bo = this.getNewObject();
			this.setCurrent(bo);
		}
		return this.current;
	}

	/**
	 * Set the current object of model, if this object doesn't exist in model,
	 * will be added to model too.
	 * 
	 * @param current
	 */
	public void setCurrent(BO current) {
		this.current = current;
		this.add(current);
	}

	/**
	 * Remove current object from list, find the object by object id, if the
	 * list is empty after removal, set current to null, If list is not empty
	 * set current to the first element of list; Use when delete object;
	 */
	public void remove(BO object) {
		for (int i = 0; i < list.size(); i++) {
			if (list.get(i).equalsId(object)) {
				if (list.get(i).equalsId(this.current)) {
					list.remove(i);
					if (list.size() > 0) {
						if (i < list.size()) {
							this.current = list.get(i);
						} else {
							this.current = list.get(0);
						}
					} else {
						this.current = null;
					}
				} else {
					list.remove(i);
				}
				break;
			}
		}
	}

	/**
	 * Remove all objects from list, please see remove(BO object) for detail
	 * remove operation;
	 * 
	 * @param objects
	 */
	public void removeAll(List<BO> objects) {
		for (BO o : objects) {
			this.remove(o);
		}
	}

	/**
	 * Use object's Id to find an equal object in current list, if can not find @param
	 * object will be appended to list, or add operation will set that list
	 * element point to @param object; New object will always be added to first
	 * element.
	 * 
	 * @param object
	 */

	public void add(BO object) {
		if (object == null) {
			return; // null should never add to list
		}

		if (object.getIsNew() == true) {
			if (list != null && list.size() > 0
					&& list.get(0).getIsNew() == true) { // Only one new object
															// can exist in
															// list, and is
															// always the first
															// element
				list.set(0, object);
				return;
			}
			list.add(0, object); // New object should always be added to first
									// of
									// list;
			return;
		}
		boolean exist = false;
		for (int i = 0; i < this.list.size(); i++) {
			if (list.get(i).equalsId(object)) {
				exist = true;
				list.set(i, object);
				break;
			}
		}
		if (exist == false) {
			this.list.add(object);
		}
	}

	/**
	 * Those objects that does not exist in list will be add to list, for those
	 * objects exist in list add operation will point those elements to
	 * correspondent(BY Id) elements in object, Use object's id to find
	 * identical object;
	 * 
	 * @param objects
	 */

	public void addAll(List<BO> objects) {
		for (BO o : objects) {
			this.add(o);
		}
	}

	/**
	 * 
	 * @return:Selected elements in list;
	 */

	public List<BO> getSelecteds() {
		if(this.filteredList == null || this.filteredList.size() == 0){
			return new CollectionUtil<BO>().extractSelected(this
				.getList());
		}else{
			return new CollectionUtil<BO>().extractSelected(this.getFilteredList());
		}
	
	}

	/**
	 * Use @param baseObject's ID to find the BaseObject in list
	 * 
	 * @param baseObject
	 * @return
	 */

	public BO lookUp(BO baseObject) {
		for (BO bo : this.list) {
			if (bo.equalsId(baseObject)) {
				return bo;
			}
		}
		return null;
	}

	/**
	 * Sub class must implement this method to create a new object of their type
	 * For example: User,Role etc. This is the only way to create a new domain
	 * object;
	 * 
	 * @return
	 */
	public abstract BO getNewObject();

}
